file: append "target" for symbolic links
authorPaul Donald <newtwen+github@gmail.com>
Sun, 9 Nov 2025 15:11:46 +0000 (16:11 +0100)
committerRobert Marko <robimarko@gmail.com>
Mon, 10 Nov 2025 14:14:21 +0000 (15:14 +0100)
When list encounters a symbolic link, we add its nested "target" entry, to where
the symbolic link points. Just how `ls -l` reports.

So when calling e.g.: ubus call file list '{"path":"/etc"}'

instead of:

...
"name": "os-release",
"type": "symlink",
"size": 21,
"mode": 41471,
"atime": 1760891865,
"mtime": 1760891865,
"ctime": 1760891865,
"inode": 266,
"uid": 0,
"gid": 0,
"target_type": "file"
...

We get:

...
"name": "os-release",
"type": "symlink",
"size": 21,
"mode": 41471,
"atime": 1760891865,
"mtime": 1760891865,
"ctime": 1760891865,
"inode": 266,
"uid": 0,
"gid": 0,
"target": {
"name": "../usr/lib/os-release",
"type": "file",
"size": 582,
"mode": 33188,
"atime": 1760891865,
"mtime": 1760891865,
"ctime": 1760891865,
"inode": 1744,
"uid": 0,
"gid": 0
}
...

And should a link be broken:

"name": "foo",
"type": "symlink",
"size": 8,
"mode": 41471,
"atime": 1762640365,
"mtime": 1762640365,
"ctime": 1762640365,
"inode": 348,
"uid": 0,
"gid": 0,
"target": {
"name": "/tmp/foo",
"type": "broken"
}

Tested on: OpenWrt 24.10.4 r28959-29397011cc
Signed-off-by: Paul Donald <newtwen+github@gmail.com>
Tested-by: Eric Fahlgren <ericfahlgren@gmail.com>
Link: https://github.com/openwrt/rpcd/pull/21
Signed-off-by: Robert Marko <robimarko@gmail.com>
file.c

diff --git a/file.c b/file.c
index dc9c88a41890ea3affe2bf7c1df7869030e4f2c8..89ba6b4934d1383aca18fb5b405da381157d4a4c 100644 (file)
--- a/file.c
+++ b/file.c
@@ -537,12 +537,26 @@ rpc_file_list(struct ubus_context *ctx, struct ubus_object *obj,
 
                        // add target type only for symlinks
                        if (S_ISLNK(s.st_mode)) {
+                               char tbuf[PATH_MAX + 1];
+                               ssize_t tlen;
+                               void *t;
+
+                               // open nested table "target" for symbolic link
+                               t = blobmsg_open_table(&buf, "target");
+
+                               tlen = readlink(entrypath, tbuf, sizeof(tbuf) - 1);
+                               if (tlen >= 0) {
+                                       tbuf[tlen] = '\0';
+                               }
+                               blobmsg_add_string(&buf, "name", tbuf);
+
                                struct stat target;
                                if (!stat(entrypath, &target)) {
-                                       blobmsg_add_string(&buf, "target_type", d_types[_get_stat_type(&target)]);
+                                       _rpc_file_add_stat(&target);
                                } else {
-                                       blobmsg_add_string(&buf, "target_type", "broken");
+                                       blobmsg_add_string(&buf, "type", "broken");
                                }
+                               blobmsg_close_table(&buf, t);
                        }
                        blobmsg_close_table(&buf, d);
                }